package com.lincoln.system.app;

import com.lincoln.bean.ApiCode;
import com.lincoln.bean.ApiCodeException;
import com.lincoln.bean.BaseApi;
import com.lincoln.entity.client.DbClient;
import com.lincoln.entity.role.DbRole;
import com.lincoln.entity.user.DbUser;
import com.lincoln.repository.base.BaseRepositoryFactoryBean;
import com.lincoln.service.PermissionService;
import com.lincoln.service.RoleService;
import com.lincoln.service.TokenClientService;
import com.lincoln.service.UserService;
import com.lincoln.utils.RestTemplateUtils;
import com.lincoln.utils.WebParamUtils;
import org.apache.commons.lang.StringUtils;
import org.json.JSONObject;
import org.json.XML;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.lincoln.bean.BaseApi.getRequest;

@SpringBootApplication
@EnableDiscoveryClient
@EntityScan({"com.lincoln.entity.*.*", "com.lincoln.entity"})
@EnableJpaRepositories(basePackages = {"com.lincoln.repository"},
        repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class//指定自己的工厂类
)
@ComponentScan(basePackages = "com.lincoln.*")
@CrossOrigin
@RestController
public class SystemAppApplication {

    public static void main(String[] args) {
        SpringApplication.run(SystemAppApplication.class, args);
    }

    public static String api_modele_name = "auth-app";
    public static String base_url = "http://" + api_modele_name;

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

    @Autowired
    RestTemplate restTemplate;
    @Autowired
    UserService userService;
    @Autowired
    TokenClientService tokenClientService;
    @Autowired
    RoleService roleService;
    @Autowired
    PermissionService permissionService;

    @PostMapping("/system/login")
    public ApiCode login() {
        Map<String, String> param = WebParamUtils.formatRequestMap(getRequest().getParameterMap());
        Map<String, Object> params = new HashMap<>();
        for (String key : param.keySet()) {
            params.put(key, param.get(key));
        }
        params.put("grant_type", "password");
        params.put("clientId", "system-app");
        params.put("clientSecret", "systemapp");
        String xml = RestTemplateUtils.sendPost(restTemplate,"system-app", "systemapp", base_url + "/oauth/token", params, String.class);
        JSONObject xmlJSONObj = XML.toJSONObject(xml.replace("<xml>", "").replace("</xml>", ""));
        Map<String, Object> map = xmlJSONObj.toMap();
        ApiCode apiCode = ApiCode.findCode(ApiCode.SUCCESS);
        apiCode.setData(map);
        return apiCode;
    }

    @PostMapping("/system/refresh")
    public ApiCode refresh() throws ApiCodeException {
        Map<String, String> param = WebParamUtils.formatRequestMap(getRequest().getParameterMap());
        if(param.size()!=1||param.get("refresh_token")==null){
            throw new ApiCodeException(ApiCode.PARAME_NEED);
        }
        Map<String, Object> params = new HashMap<>();
        params.put("grant_type", "refresh_token");
        params.put("clientId", "system-app");
        params.put("clientSecret", "systemapp");
        params.put("refresh_token", param.get("refresh_token"));
        String xml = RestTemplateUtils.sendPost(restTemplate,"system-app", "systemapp", base_url + "/oauth/token", params, String.class);
        JSONObject xmlJSONObj = XML.toJSONObject(xml.replace("<xml>", "").replace("</xml>", ""));
        Map<String, Object> map = xmlJSONObj.toMap();
        ApiCode apiCode = ApiCode.findCode(ApiCode.SUCCESS);
        apiCode.setData(map);
        return apiCode;
    }

    /**
     * 传入内置超级密码(XXXqwer1234)
     */
    @PutMapping("/system/client")
    public ApiCode addClient(DbClient dbClient, String superPwd) throws Exception {
        hasAdmin();
        if (!"XXXqwer1234".equals(superPwd)) {
            return ApiCode.build(ApiCode.FAIL, "权限不足!");
        }
        if (StringUtils.isEmpty(dbClient.getClientId())) {
            return ApiCode.build(ApiCode.FAIL, "client_id can not be null!");
        }
        if (StringUtils.isEmpty(dbClient.getResourceIds())) {
            return ApiCode.build(ApiCode.FAIL, "resource_ids can not be null!");
        }
        if (StringUtils.isEmpty(dbClient.getClientSecret())) {
            return ApiCode.build(ApiCode.FAIL, "client_secret can not be null!");
        }
        if (StringUtils.isEmpty(dbClient.getScope())) {
            return ApiCode.build(ApiCode.FAIL, "scope can not be null!");
        }
        dbClient.setAuthorizedGrantTypes("authorization_code,password,refresh_token,implicit");
        List<DbClient> ex = tokenClientService.findByUnique("clientId", dbClient.getClientId());
        if (ex != null && ex.size() > 0) {
            return ApiCode.build(ApiCode.FAIL, "client_id is already existed!");
        }
        //加密密码
        dbClient.setClientSecret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(dbClient.getClientSecret()));
        //检测scope(英文逗号分割)
        String[] scopes = dbClient.getScope().split(",");
        for (String scope : scopes) {
            List<DbRole> role = roleService.findByUnique("name", scope);
            if (role == null || role.size() <= 0) {
                return ApiCode.build(ApiCode.FAIL, "scope is not existed!");
            }
        }
        //检测authorized_grant_types
        String[] authorizedGrantTypes = dbClient.getAuthorizedGrantTypes().split(",");
        for (String authorizedGrantType : authorizedGrantTypes) {
            if (!"authorization_code".equals(authorizedGrantType) && !"password".equals(authorizedGrantType) && !"refresh_token".equals(authorizedGrantType) && !"implicit".equals(authorizedGrantType)) {
                return ApiCode.build(ApiCode.FAIL, "authorization_code is not existed!");
            }
        }
        if (dbClient.getAccessTokenValidity() == null) {
            dbClient.setAccessTokenValidity(900);
        }
        if (dbClient.getRefreshTokenValidity() == null) {
            dbClient.setRefreshTokenValidity(3600);
        }
        tokenClientService.save(dbClient);
        return ApiCode.findCode(ApiCode.SUCCESS);
    }

    @PutMapping("/system/role")
    public ApiCode addRole(String name, String permissions, String superPwd) throws Exception {
        hasAdmin();
        if (!"XXXqwer1234".equals(superPwd)) {
            return ApiCode.build(ApiCode.FAIL, "权限不足!");
        }
        if (StringUtils.isEmpty(name)) {
            return ApiCode.build(ApiCode.FAIL, "name can not be null!");
        }
        if (StringUtils.isEmpty(permissions)) {
            return ApiCode.build(ApiCode.FAIL, "permissions can not be null!");
        }
        roleService.save(name, permissions);
        return ApiCode.findCode(ApiCode.SUCCESS);
    }

    @PutMapping("/system/permission")
    public ApiCode addPermission(String name, String superPwd) throws Exception {
        hasAdmin();
        if (!"XXXqwer1234".equals(superPwd)) {
            return ApiCode.build(ApiCode.FAIL, "权限不足!");
        }
        if (StringUtils.isEmpty(name)) {
            return ApiCode.build(ApiCode.FAIL, "name can not be null!");
        }
        String[] names = name.split(",");
        permissionService.savePermissions(names);
        return ApiCode.findCode(ApiCode.SUCCESS);
    }

    @PutMapping("/system/user")
    public ApiCode addUser(DbUser user, String superPwd) throws Exception {
        hasAdmin();
        if (!"XXXqwer1234".equals(superPwd)) {
            return ApiCode.build(ApiCode.FAIL, "权限不足!");
        }
        if (StringUtils.isEmpty(user.getUserName())) {
            return ApiCode.build(ApiCode.FAIL, "userName can not be null!");
        }
        if (StringUtils.isEmpty(user.getPassword())) {
            return ApiCode.build(ApiCode.FAIL, "password can not be null!");
        }
        if (user.getRole().getId() == null) {
            return ApiCode.build(ApiCode.FAIL, "roleId can not be null!");
        }
        user.setPassword(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(user.getPassword()));
        DbRole role = roleService.findById(user.getRole().getId());
        user.setRole(role);
        user.setAccountNonLocked(true);
        user.setEnabled(true);
        user.setAccountNonExpired(true);
        user.setCredentialsNonExpired(true);
        userService.save(user);
        return ApiCode.findCode(ApiCode.SUCCESS);
    }

    public boolean hasAdmin() throws ApiCodeException {
        if (userService.findAll().size() > 0) {
            if (BaseApi.getAuthorities().size() <= 0) {
                throw new ApiCodeException(ApiCode.NOPERMISSION);
            }
            boolean flag = false;
            for (GrantedAuthority grantedAuthority : BaseApi.getAuthorities()) {
                if ("role_admin".equals(grantedAuthority.getAuthority())) {
                    flag = true;
                }
            }
            if (flag == false) {
                throw new ApiCodeException(ApiCode.NOPERMISSION);
            }
            return true;
        } else {
            return true;
        }
    }
}
